<?php

namespace addons\yunfood\controller;

use addons\yunfood\library\Constant;
use addons\yunfood\library\Logic;
use addons\yunfood\library\Super;
use addons\yunfood\model\Config;
use addons\yunfood\model\FoodLike;
use addons\yunfood\model\FundUser;
use addons\yunfood\model\SmsCode;
use addons\yunfood\model\UserAuth;
use addons\yunfood\model\User as UserModel;
use addons\yunfood\model\Order;
use fast\Random;
use think\Validate;

/**
 * 用户接口
 */
class User extends BaseApi
{
    public $noNeedLogin = [
        'wechatAutologin',
        'mpAutologin',
        'loginByPhoneCaptcha',
        'loginByWechatPhone',
        'sendPhoneCaptcha'
    ];

    /**
     * 微信小程序code登陆
     */
    public function wechatAutologin()
    {
        $wxCode = $this->request->post('wx_code');
        if (! $wxCodeData = Logic::getMiniCodeData($wxCode, false)) {
            $this->success('认证失败', null, Constant::IGNORE_ERROR);
        }
        $this->tryUserAuthLogin($wxCodeData['openid'], UserAuth::SOURCE_MINI);
        $this->success('操作成功', null, Constant::IGNORE_ERROR);
    }

    /**
     * 微信网页authKey登陆
     */
    public function mpAutologin()
    {
        $authKey = $this->request->post('auth_key');
        if (! $openid = Logic::getUserAuthOpenid($authKey, UserAuth::SOURCE_MP)) {
            $this->success('认证失败', null, Constant::IGNORE_ERROR);
        }
        $this->tryUserAuthLogin($openid, UserAuth::SOURCE_MP);
        $this->success('操作成功');
    }

    /**
     * 登录方式 - 手机验证码
     */
    public function loginByPhoneCaptcha()
    {
        $data = $this->request->post();
        $validate = new Validate(['phone' => 'require', 'captcha' => 'require']);
        if (! $validate->check($data)) {
            $this->error($validate->getError());
        }
        $phone = trim($data['phone']);
        $wxCode = $this->request->post('wx_code');
        $authKey = $this->request->post('auth_key');

        // 检查登录方式
        $this->checkLoginMethod('phone_captcha');

        // 检查微信code
        $openid = $source = null;
        if ($wxCode) {
            $source = UserAuth::SOURCE_MINI;
            if ($wxCodeData = $this->checkWxCode($wxCode)) {
                $openid = $wxCodeData['openid'];
            }
        } elseif ($authKey) {
            $openid = Logic::getUserAuthOpenid($authKey, UserAuth::SOURCE_MP);
            $source = UserAuth::SOURCE_MP;
        }

        // 校验验证码
        $smsCode = SmsCode::where([
            'phone' => $phone,
            'code'  => trim($data['captcha'])
        ])->order(['created_at' => SORT_DESC])->find();
        if (! $smsCode) {
            $this->error(__('验证码错误'));
        }
        if ($smsCode->expired_at < time() || $smsCode->status != SmsCode::STATE_ING) {
            $this->error(__('验证码失效'));
        }
        $smsCode->status = SmsCode::STATE_END;
        $smsCode->save();

        // 尝试注册登录
        $this->tryUserPhoneLogin($phone, $openid, $source);
    }

    /**
     * 登录方式 - 微信手机号
     */
    public function loginByWechatPhone()
    {
        $data = $this->request->post();
        $validate = new Validate(['encrypted_data' => 'require', 'iv' => 'require', 'wx_code' => 'require']);
        if (! $validate->check($data)) {
            $this->error($validate->getError());
        }
        $encryptedData = $this->request->post("encrypted_data", '', 'trim');;
        $iv = $data['iv'];
        $wxCode = $data['wx_code'];

        // 检查登录方式
        $this->checkLoginMethod('wechat_phone');

        // 获取微信code
        if (! $wxCodeData = Logic::getMiniCodeData($wxCode, false)) {
            $this->error(__('获取微信登录信息失败'));
        }
        $openid = $wxCodeData['openid'];
        $source = UserAuth::SOURCE_MINI;

        // 解密手机号
        if ($phoneCode = ($data['code'] ?? null)) {
            if (! $result = Logic::getWechatPhoneData($phoneCode)) {
                $this->error(__('获取微信手机号失败'));
            }
        } else {
            if (! $result = Logic::decryptWechatData($wxCodeData['session_key'], $encryptedData, $iv)) {
                $this->error(__('获取微信手机号失败'));
            }
        }

        // 尝试注册登录
        $this->tryUserPhoneLogin($result['phoneNumber'], $openid, $source);
    }

    /**
     * 发送手机验证码
     */
    public function sendPhoneCaptcha()
    {
        $data = $this->request->post();
        $validate = new Validate(['phone' => 'require']);
        if (! $validate->check($data)) {
            $this->error($validate->getError());
        }
        $phone = trim($data['phone']);

        // 检查登录方式
        $this->checkLoginMethod('phone_captcha');

        // 检查微信code
        $wxCodeData = $this->checkWxCode($data['wx_code'] ?? null);

        // 校验手机号发送验证码次数
        $recordList = SmsCode::where(['phone' => $phone])
            ->where('created_at', '>=', strtotime('today'))
            ->order(['created_at' => SORT_DESC])->select();
        if (count($recordList) > 0) {
            if ($recordList[0]->created_at > time() - 60) {
                $this->error(__('一分钟之内只能发送一次验证码'));
            }
        }
        if (count($recordList) >= 10) {
            $this->error(__('今日验证码发送次数已达上限'));
        }

        // 微信用户发送验证码次数限制
        if ($wxCodeData) {
            $userCount = SmsCode::where(['openid' => $wxCodeData['openid']])
                ->where('created_at', '>=', strtotime('today'))->count();
            if ($userCount >= 20) {
                $this->error(__('今日验证码发送次数已达上限'));
            }
        }

        // 保存验证码
        $code = random_int(100000, 999999);
        $smsCode = new SmsCode();
        $smsCode->openid = $wxCodeData['openid'] ?? null;
        $smsCode->phone = $phone;
        $smsCode->code = $code;
        $smsCode->status = SmsCode::STATE_ING;
        $smsCode->expired_at = time() + 300;
        $smsCode->save();

        if ($error = Logic::sendPhoneCaptcha($phone, $code)) {
            $this->error(__('短信发送失败: ' . $error));
        }

        $this->success('操作成功', null);
    }

    /**
     * 退出登录
     */
    public function logout()
    {
        // 暂不解绑，会导致用户找不到原来的账号
//        if ($source = $this->request->post('source')) {
//            Logic::unbindUserAuth($this->auth->id, $source);
//        }
        $this->auth->logout();
        $this->success(__('退出成功'));
    }

    /**
     * 更新用户信息
     */
    public function updateUserInfo()
    {
        $update = [];

        // 修改头像
        if ($avatarUrl = $this->request->post('avatar_url')) {
            $update['avatar'] = $avatarUrl;
        }

        // 修改性别
        $gender = $this->request->post('gender');
        if ($gender !== null) {
            $update['gender'] = $gender;
        }

        // 修改数据
        if ($update) {
            UserModel::update($update, ['id' => $this->userId]);
        }

        $this->success(__('成功'));
    }

    /**
     * 获取用户信息
     */
    public function getUserInfo()
    {
        $userInfo = UserModel::get($this->userId);
        $result = Super::arrayGetSet($userInfo, [
            'username',
            'nickname',
            'mobile',
            'gender',
            'gender_text',
            'avatar_url' => function ($item) {
                return cdnurl($item['avatar'], true);
            },
        ]);
        if ($fundUser = FundUser::get(['user_id' => $this->userId])) {
            $result['fund_state'] = $fundUser->status;
            $result['fund_amount_left'] = $fundUser->amount_left;
        } else {
            $result['fund_state'] = 0;
            $result['fund_amount_left'] = 0;
        }

        $result['count_order'] = Order::where(['user_id' => $this->userId])->count();
        $result['count_like'] = FoodLike::where(['user_id' => $this->userId])->count();

        $this->success(__('查询成功'), $result);
    }

    /**
     * 修改手机号
     */
    public function changePhone()
    {
        $data = $this->request->post();
        $validate = new Validate(['phone' => 'require', 'captcha' => 'require']);
        if (! $validate->check($data)) {
            $this->error($validate->getError());
        }
        $newPhone = trim($data['phone']);

        // 检查登录方式
        $this->checkLoginMethod('phone_captcha', '不支持修改');

        // 校验验证码
        $smsCodeList = SmsCode::where([
            'phone' => $newPhone,
            'code'  => trim($data['captcha'])
        ])->order(['created_at' => SORT_DESC])->limit(1)->select();
        if (count($smsCodeList) == 0) {
            $this->error(__('验证码错误'));
        }
        $smsCode = $smsCodeList[0];
        if ($smsCode->expired_at < time() || $smsCode->status != SmsCode::STATE_ING) {
            $this->error(__('验证码失效'));
        }
        $smsCode->status = SmsCode::STATE_END;
        $smsCode->save();

        if ($this->userData->mobile == $newPhone) {
            $this->error(__('不能与当前手机号相同'));
        }

        // 唯一性校验
        if (UserModel::get(['mobile' => $newPhone])) {
            $this->error(__('手机号已被占用，请重新换一个试试'));
        }

        // 修改手机号
        $this->userData->username = $newPhone;
        $this->userData->mobile = $newPhone;
        $this->userData->save();
        $this->success();
    }

    /**
     * 修改手机号 - 授权微信手机号
     */
    public function changeWechatPhone()
    {
        $data = $this->request->post();
        $validate = new Validate(['encrypted_data' => 'require', 'iv' => 'require', 'wx_code' => 'require']);
        if (! $validate->check($data)) {
            $this->error($validate->getError());
        }
        $encryptedData = $this->request->post("encrypted_data", '', 'trim');;
        $iv = $data['iv'];
        $wxCode = $data['wx_code'];

        // 检查登录方式
        $this->checkLoginMethod('wechat_phone', '不支持修改');

        // 获取微信code
        if (! $wxCodeData = Logic::getMiniCodeData($wxCode, false)) {
            $this->error(__('获取微信登录信息失败'));
        }

        // 解密手机号
        if ($phoneCode = ($data['code'] ?? null)) {
            if (! $result = Logic::getWechatPhoneData($phoneCode)) {
                $this->error(__('获取微信手机号失败'));
            }
        } else {
            if (! $result = Logic::decryptWechatData($wxCodeData['session_key'], $encryptedData, $iv)) {
                $this->error(__('获取微信手机号失败'));
            }
        }

        $newPhone = $result['phoneNumber'];
        if ($this->userData->mobile == $newPhone) {
            $this->error(__('不能与当前手机号相同'));
        }

        // 唯一性校验
        if (UserModel::get(['mobile' => $newPhone])) {
            $this->error(__('手机号已被占用，请重新换一个试试'));
        }

        // 修改手机号
        $this->userData->username = $newPhone;
        $this->userData->mobile = $newPhone;
        $this->userData->save();
        $this->success();
    }

    /**
     * 修改昵称
     */
    public function changeNickname()
    {
        $data = $this->request->post();
        $validate = new Validate(['nickname' => 'require']);
        if (! $validate->check($data)) {
            $this->error($validate->getError());
        }
        $newNickname = trim($data['nickname']);

        // 检查登录方式
        $this->checkLoginMethod('phone_captcha', '不支持修改');

        if ($this->userData->nickname == $newNickname) {
            $this->error(__('不能与当前昵称相同'));
        }

        // 唯一性校验
        if (UserModel::get(['nickname' => $newNickname])) {
            $this->error(__('昵称已被占用，请重新换一个试试'));
        }

        // 修改昵称
        $this->userData->nickname = $newNickname;
        $this->userData->save();
        $this->success();
    }

    /**
     * 保存订阅
     */
    public function saveSubscribe()
    {
        $this->success(__('success'));
    }

    /**
     * 检查登录方式
     *
     * @param $loginMethod
     */
    private function checkLoginMethod($loginMethod, $tips = '不支持当前登录方式')
    {
        if (is_string($loginMethod)) {
            $loginMethod = [$loginMethod];
        }
        $loginMethods = Config::getGroupAttribute(Config::GROUP_BASE, 'login_methods');
        if (! $loginMethods) {
            $this->error(__($tips));
        }
        $loginMethods = explode(',', $loginMethods);
        if (! array_intersect($loginMethods, $loginMethod)) {
            $this->error(__($tips));
        }
    }

    /**
     * 检查登录code
     *
     * @param $wxCode
     */
    private function checkWxCode($wxCode)
    {
        if (Config::getGroupAttribute(Config::GROUP_BASE, 'only_wechat')) {
            if (! $wxCode) {
                $this->error(__('微信登录code不能为空'));
            }
        }
        if ($wxCode) {
            if (! $wxCodeData = Logic::getMiniCodeData($wxCode, false)) {
                $this->error(__('获取微信登录信息失败'));
            }

            return $wxCodeData;
        }

        return null;
    }

    // 尝试授权信息登录
    private function tryUserAuthLogin($openid, $source)
    {
        if ($userId = Logic::getUserAuthUid($openid, $source)) {
            $user = UserModel::get($userId);
            if ($user && $user->status == UserModel::STATUS_NORMAL) {
                if ($this->auth->direct($userId)) {
                    $userInfo = $this->auth->getUserInfo();
                    if ($source == UserAuth::SOURCE_MP) {
                        $userInfo['openid'] = $openid;
                    }
                    $this->success('登陆成功', $userInfo);
                }
            }
            Logic::unbindUserAuth($userId, $source);
            $this->success('登陆失败', null, Constant::IGNORE_ERROR);
        } else {
            $user = UserModel::get(['username' => $openid]);
            if ($user) {
                $this->auth->direct($user->id);
            } else {
                $username = $openid;
                $password = Random::alnum(8);
                $result = $this->auth->register($username, $password, '', '',
                    ['nickname' => Logic::generateBusinessNo('nickname'), 'gender' => UserModel::GENDER_NONE]);
                if (! $result) {
                    $this->error($this->auth->getError());
                }
                if (! $user = $this->auth->getUser()) {
                    $this->error(__('登录失败，请稍后重试'));
                }
            }
            Logic::bindUserAuth($user->id, $openid, $source);
            $userInfo = $this->auth->getUserInfo();
            if ($source == UserAuth::SOURCE_MP) {
                $userInfo['openid'] = $openid;
            }
            $this->success('登陆成功', $userInfo);
        }
    }

    // 尝试手机号登录
    private function tryUserPhoneLogin($phone, $openid, $source)
    {
        // 查找用户
        if (! $user = UserModel::get(['mobile' => $phone])) {
            $username = $phone;
            $password = Random::alnum(8);
            $result = $this->auth->register($username, $password, '', $phone, ['gender' => UserModel::GENDER_NONE]);
            if (! $result) {
                $this->error($this->auth->getError());
            }
            if (! $user = $this->auth->getUser()) {
                $this->error(__('登录失败，请稍后重试'));
            }
        } else {
            if ($user->status != UserModel::STATUS_NORMAL) {
                $this->error(__('当前用户被禁用，请联系管理员处理'));
            }
            if (! $this->auth->direct($user->id)) {
                $this->error($this->auth->getError());
            }
        }

        Logic::bindUserAuth($user->id, $openid, $source);
        $this->success(__('登录成功'), $this->auth->getUserInfo());
    }
}
